package wiki.xsx.core.handler;

import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.SerializationException;

import java.util.Arrays;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 键助手
 * @author xsx
 * @date 2019/4/25
 * @since 1.8
 */
public class KeyHandler {
    /**
     * 对象模板
     */
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 字符串模板
     */
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 键助手构造
     * @param redisTemplate 对象模板
     * @param stringRedisTemplate 字符串模板
     */
    private KeyHandler(RedisTemplate<String, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) {
        this.redisTemplate = redisTemplate;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 获取实例
     * @param redisTemplate 对象模板
     * @param stringRedisTemplate 字符串模板
     * @return 返回实例
     */
    public static KeyHandler getInstance(RedisTemplate<String, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) {
        return new KeyHandler(redisTemplate, stringRedisTemplate);
    }

    /**
     * 是否存在key(对象)
     * <p>EXISTS key [key ...]</p>
     * @since redis 1.0.0
     * @param key 键
     * @return 返回布尔值,存在true,不存在false
     */
    public Boolean hasKeyAsObj(String key) {
        return this.redisTemplate.hasKey(key);
    }

    /**
     * 是否存在key(字符串)
     * <p>EXISTS key [key ...]</p>
     * @since redis 1.0.0
     * @param key 键
     * @return 返回布尔值,存在true,不存在false
     */
    public Boolean hasKey(String key) {
        return this.stringRedisTemplate.hasKey(key);
    }

    /**
     * 移除对象key
     * <p>DEL key [key ...]</p>
     * @since redis 1.0.0
     * @param keys 键
     */
    public void removeAsObj(String ...keys) {
        this.redisTemplate.delete(Arrays.asList(keys));
    }

    /**
     * 移除字符串key
     * <p>DEL key [key ...]</p>
     * @since redis 1.0.0
     * @param keys 键
     */
    public void remove(String ...keys) {
        this.stringRedisTemplate.delete(Arrays.asList(keys));
    }


    /**
     * 设置对象过期时间
     * <p>EXPIRE key seconds</p>
     * @since redis 1.0.0
     * @param key 键
     * @param timeout 过期时间
     * @param timeUnit 时间单位
     * @return 返回布尔值,成功true,失败false
     */
    public Boolean expireAsObj(String key, long timeout, TimeUnit timeUnit) {
        return this.redisTemplate.expire(key, timeout, timeUnit);
    }

    /**
     * 设置字符串过期时间
     * <p>EXPIRE key seconds</p>
     * @since redis 1.0.0
     * @param key 键
     * @param timeout 过期时间
     * @param timeUnit 时间单位
     * @return 返回布尔值,成功true,失败false
     */
    public Boolean expire(String key, long timeout, TimeUnit timeUnit) {
        return this.stringRedisTemplate.expire(key, timeout, timeUnit);
    }

    /**
     * 设置对象过期时间
     * <p>EXPIREAT key timestamp</p>
     * @since redis 1.2.0
     * @param key 键
     * @param date 过期时间
     * @return 返回布尔值,成功true,失败false
     */
    public Boolean expireAtAsObj(String key, Date date) {
        return this.redisTemplate.expireAt(key, date);
    }

    /**
     * 设置字符串过期时间
     * <p>EXPIREAT key timestamp</p>
     * @since redis 1.2.0
     * @param key 键
     * @param date 过期时间
     * @return 返回布尔值,成功true,失败false
     */
    public Boolean expireAt(String key, Date date) {
        return this.stringRedisTemplate.expireAt(key, date);
    }

    /**
     * 获取对象过期时间
     * <p>TTL key</p>
     * @since redis 1.0.0
     * @param key 键
     * @param timeUnit 时间单位
     * @return 返回对象过期时间
     */
    public Long getExpireAsObj(String key, TimeUnit timeUnit) {
        return this.redisTemplate.getExpire(key, timeUnit);
    }

    /**
     * 获取字符串过期时间
     * <p>TTL key</p>
     * @since redis 1.0.0
     * @param key 键
     * @param timeUnit 时间单位
     * @return 返回字符串过期时间
     */
    public Long getExpire(String key, TimeUnit timeUnit) {
        return this.stringRedisTemplate.getExpire(key, timeUnit);
    }

    /**
     * 获取对象存储数据类型
     * <p>TYPE key</p>
     * @since redis 1.0.0
     * @param key 键
     * @return 返回对象存储数据类型
     */
    public DataType getTypeAsObj(String key) {
        return this.redisTemplate.type(key);
    }

    /**
     * 获取字符串存储数据类型
     * <p>TYPE key</p>
     * @since redis 1.0.0
     * @param key 键
     * @return 返回字符串存储数据类型
     */
    public DataType getType(String key) {
        return this.stringRedisTemplate.type(key);
    }

    /**
     * 对象的键集合
     * <p><p>EXISTS key [key ...]</p></p>
     * @since redis 1.0.0
     * @param pattern 键规则
     * @return 返回对象键的集合
     */
    public Set<String> keysAsObj(String pattern) {
        return this.redisTemplate.keys(pattern);
    }

    /**
     * 字符串的键集合
     * <p><p>EXISTS key [key ...]</p></p>
     * @since redis 1.0.0
     * @param pattern 键规则
     * @return 返回字符串键的集合
     */
    public Set<String> keys(String pattern) {
        return this.stringRedisTemplate.keys(pattern);
    }

    /**
     * 获取对象随机key
     * <p>RANDOMKEY</p>
     * @since redis 1.0.0
     * @return 返回对象随机的键
     */
    public String randomKeyAsObj() {
        String o;
        try {
            o = this.redisTemplate.randomKey();
        }catch(SerializationException e){
            o = null;
        }
        return o;
    }

    /**
     * 获取字符串随机key
     * <p>RANDOMKEY</p>
     * @since redis 1.0.0
     * @return 返回字符串随机的键
     */
    public String randomKey() {
        return this.stringRedisTemplate.randomKey();
    }

    /**
     * 重命名对象key
     * <p>RENAME key newkey</p>
     * @since redis 1.0.0
     * @param oldKey 旧key
     * @param newKey 新key
     */
    public void renameAsObj(String oldKey, String newKey) {
        this.redisTemplate.rename(oldKey, newKey);
    }

    /**
     * 重命名字符串key
     * <p>RENAME key newkey</p>
     * @since redis 1.0.0
     * @param oldKey 旧key
     * @param newKey 新key
     */
    public void rename(String oldKey, String newKey) {
        this.stringRedisTemplate.rename(oldKey, newKey);
    }

    /**
     * 重命名对象key如果存在
     * <p>RENAMENX key newkey</p>
     * @since redis 1.0.0
     * @param oldKey 旧key
     * @param newKey 新key
     * @return 返回布尔值,成功true,失败false
     */
    public Boolean renameAsObjIfAbsent(String oldKey, String newKey) {
        return this.redisTemplate.renameIfAbsent(oldKey, newKey);
    }

    /**
     * 重命名字符串key如果存在
     * <p>RENAMENX key newkey</p>
     * @since redis 1.0.0
     * @param oldKey 旧key
     * @param newKey 新key
     * @return 返回布尔值,成功true,失败false
     */
    public Boolean renameIfAbsent(String oldKey, String newKey) {
        return this.stringRedisTemplate.renameIfAbsent(oldKey, newKey);
    }

    /**
     * 持久化对象
     * <p>PERSIST key</p>
     * @since redis 2.2.0
     * @param key 键
     * @return 返回布尔值,成功true,失败false
     */
    public Boolean persistAsObj(String key){
        return this.redisTemplate.persist(key);
    }

    /**
     * 持久化字符串
     * <p>PERSIST key</p>
     * @since redis 2.2.0
     * @param key 键
     * @return 返回布尔值,成功true,失败false
     */
    public Boolean persist(String key){
        return this.stringRedisTemplate.persist(key);
    }

    /**
     * 移动对象到指定数据库
     * <p>MOVE key db</p>
     * @since redis 1.0.0
     * @param dbIndex 数据库索引
     * @param key 键
     * @return 返回布尔值,成功true,失败false
     */
    public Boolean moveAsObj(int dbIndex, String key){
        return this.redisTemplate.move(key, dbIndex);
    }

    /**
     * 移动字符串到指定数据库
     * <p>MOVE key db</p>
     * @since redis 1.0.0
     * @param dbIndex 数据库索引
     * @param key 键
     * @return 返回布尔值,成功true,失败false
     */
    public Boolean move(int dbIndex, String key) {
        return this.stringRedisTemplate.move(key, dbIndex);
    }

    /**
     * 当前数据库中键的数量
     * <p>DBSIZE</p>
     * @since redis 1.0.0
     * @return 返回键的数量
     */
    public Long count() {
        Long count;
        try {
            count = this.redisTemplate.getConnectionFactory().getConnection().dbSize();
        }catch (IllegalStateException ex) {
            count = this.stringRedisTemplate.getConnectionFactory().getConnection().dbSize();
        }
        return count;
    }

    /**
     * 获取序列化对象
     * <p>DUMP key</p>
     * @since redis 2.6.0
     * @param key 键
     * @return 返回字节数组
     */
    public byte[] dumpAsObj(String key) {
        return this.redisTemplate.dump(key);
    }

    /**
     * 获取序列化字符串
     * <p>DUMP key</p>
     * @since redis 2.6.0
     * @param key 键
     * @return 返回字节数组
     */
    public byte[] dump(String key) {
        return this.stringRedisTemplate.dump(key);
    }

    /**
     * 序列化存储对象
     * <p>RESTORE key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]</p>
     * @since redis 2.6.0
     * @param key 键
     * @param value 对象字节
     * @param timeout 过期时间
     * @param unit 单位
     */
    public void restoreAsObj(String key, byte[] value, Long timeout, TimeUnit unit) {
        this.redisTemplate.restore(key, value, timeout, unit);
    }

    /**
     * 序列化存储字符串
     * <p>RESTORE key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]</p>
     * @since redis 2.6.0
     * @param key 键
     * @param value 字符串字节
     * @param timeout 过期时间
     * @param unit 单位
     */
    public void restore(String key, byte[] value, Long timeout, TimeUnit unit) {
        this.stringRedisTemplate.restore(key, value, timeout, unit);
    }

    /**
     * 获取spring redis模板
     * @return 返回对象模板
     */
    public RedisTemplate getRedisTemplate() {
        return this.redisTemplate;
    }

    /**
     * 获取spring string redis模板
     * @return 返回字符串模板
     */
    public StringRedisTemplate getStringRedisTemplate() {
        return this.stringRedisTemplate;
    }
}
